
#include "Base64.h"
#include <sstream>

namespace smartvoice {

static const char g_encode[] =
	"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
	"abcdefghijklmnopqrstuvwxyz"
	"0123456789"
	"+/";
static const char g_decode[] = {
	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 62,  0,  0,  0, 63,
	52, 53, 54, 55, 56, 57, 58, 59, 60, 61,  0,  0,  0,  0,  0,  0,
	 0,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
	15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,  0,  0,  0,  0,  0,
	 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
	41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,  0,  0,  0,  0,  0,
	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
};

inline char encode(unsigned int x)
{
	return g_encode[x & 0x3f];
}

/////////////////////////////////////////////////////////////////////////////

std::string Base64::Encode(const char* data)
{
	return Encode(data, strlen(data));
}

std::string Base64::Decode(const char* data)
{
	return Decode(data, strlen(data));
}

std::string Base64::Encode(const std::string &str)
{
	return Encode(str.c_str(), str.size());
}

std::string Base64::Decode(const std::string &str)
{
	return Decode(str.c_str(), str.size());
}

std::string Base64::Encode(const char* data, unsigned int length)
{
	std::ostringstream oss;

	for (unsigned int i=0; i<length; i+=3)
	{
		unsigned int left = length - i;
		const unsigned char* start = reinterpret_cast<const unsigned char*>(data) + i;

		// byte 0
		oss << encode(start[0] >> 2);

		// byte 1
		unsigned char tmp = (start[0] << 4);
		if (left == 1)
		{
			oss << encode(tmp);
			break;
		}
		tmp |= ((start[1] >> 4) & 0xF);
		oss << encode(tmp);

		// byte 2
		tmp = ((start[1] & 0xF) << 2);
		if (left == 2)
		{
			oss << encode(tmp);
			break;
		}
		tmp |= ((start[2] >> 6) & 0x3);
		oss << encode(tmp);

		// byte 3
		oss << encode(start[2] & 0x3f);
	}

	unsigned int pad = length % 3;
	if (pad == 1)
		oss << "==";
	else if (pad == 2)
		oss << "=";

	return oss.str();
}

std::string Base64::Decode(const char* data, unsigned int length)
{
	if (length % 4 != 0)
		return ""; // INVALID BASE64

	std::ostringstream oss;

	unsigned char buff[3];
	for (unsigned int i=0; i<length; i+=4)
	{
		const unsigned char* start = reinterpret_cast<const unsigned char*>(data + i);
		buff[0] = (((g_decode[start[0]] << 2) & 0xFC) | ((g_decode[start[1]] >> 4) & 0x03));
		buff[1] = (((g_decode[start[1]] << 4) & 0xF0) | ((g_decode[start[2]] >> 2) & 0x0F));
		buff[2] = (((g_decode[start[2]] << 6) & 0xC0) | ((g_decode[start[3]]     ) & 0x3F));

		unsigned int len = 3;
		if (start[3] == '=')
		{
			len = 2;
			if (start[2] == '=')
				len = 1;
		}
		oss.write(reinterpret_cast<char*>(buff), len);
	}

	return oss.str();
}

} // namespace smartvoice

